﻿using System;
using System.Collections.Generic;
using TheDarkKnight.MeshFactory;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using static TheDarkKnight.DynamicSpwanModel.DynamicModelLibrary;
using static TheDarkKnight.TempletObjectBase;

namespace TheDarkKnight.DynamicSpwanModel {

    public class DynamicModelLibraryHP
    {
        /// <summary>
        /// 多线程计算网格顶点位置数据
        /// </summary>
        [BurstCompile]
        public struct MeshVerticesCaculate : IJobParallelForBatch
        {
            /// <summary>
            /// 路径点集合
            /// </summary>
            [ReadOnly]
            public NativeArray<float3> PathPointArray;

            /// <summary>
            /// 曲线点集合
            /// </summary>
            [ReadOnly]
            public NativeArray<float3> BeizerPointArray;

            /// <summary>
            /// 模型模板顶点结合
            /// </summary>
            [ReadOnly]
            public NativeArray<float3> TempletVerticeArray;

            /// <summary>
            /// 细分数量
            /// </summary>
            [ReadOnly]
            public int SegmentCount;

            /// <summary>
            /// 固定向量s
            /// </summary>
            [ReadOnly]
            public float3 DIRECTIONUP;

            /// <summary>
            /// U总长度
            /// </summary>
            [ReadOnly]
            public float UTotalLength;

            /// <summary>
            /// 网格的顶点集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeArray<float3> VeticesArray;

            /// <summary>
            /// 网格的法线集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeArray<float3> NormalArray;

            /// <summary>
            /// 网格的UV集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeArray<float2> UVArray;

            public void Execute(int startIndex, int count)
            {
                int templetVerticeCount = TempletVerticeArray.Length;
                int indexCount = count + startIndex;
                for (int pathIndex = startIndex; pathIndex < indexCount; pathIndex++)
                {
                    int nextPathIndex = pathIndex + 1;
                    if (nextPathIndex >= PathPointArray.Length) break;
                    float3 curNode = PathPointArray[pathIndex];
                    float3 nextNode = PathPointArray[nextPathIndex];
                    float3 bezierNode = BeizerPointArray[pathIndex];
                    for (int segmentIndex = pathIndex == 0 ? 0 : 1; segmentIndex < SegmentCount; segmentIndex++)
                    {
                        float t = segmentIndex * 1.0f / (SegmentCount - 1);
                        float3 segmentPoint = BeizerUilityTools.BezierCurve(curNode, nextNode, bezierNode, t);
                        float3 gradient = BeizerUilityTools.BezierCurveGradient(curNode, nextNode, bezierNode, t);
                        float tempLength = 0;
                        for (int verticesIndex = 0; verticesIndex < templetVerticeCount; verticesIndex++)
                        {
                            int tempVerticeCount = (pathIndex * (SegmentCount - 1) + segmentIndex) * templetVerticeCount + verticesIndex + 1;
                            float3 localPos = TempletVerticeArray[verticesIndex];
                            float3 worldPos = localToWorld(localPos, segmentPoint, gradient, DIRECTIONUP);
                            VeticesArray[tempVerticeCount] = worldPos;
                            NormalArray[tempVerticeCount] = math.normalize(TempletVerticeArray[verticesIndex] - segmentPoint);
                            if (verticesIndex > 0) tempLength += Vector3.Distance(TempletVerticeArray[verticesIndex], TempletVerticeArray[verticesIndex - 1]);
                            UVArray[tempVerticeCount] = new Vector2(tempLength / UTotalLength, 0);
                        }
                    }
                }
            }

        }
        [BurstCompile]
        public struct BeizerCurveLength : IJob
        {

            /// <summary>
            /// 路径点集合
            /// </summary>
            [ReadOnly]
            public NativeArray<float3> PathPointArray;

            /// <summary>
            /// 曲线点集合
            /// </summary>
            [ReadOnly]
            public NativeArray<float3> BeizerPointArray;

            /// <summary>
            /// 顶点总数
            /// </summary>
            [ReadOnly]
            public int PathCount;

            /// <summary>
            /// 贝塞尔曲线总长度
            /// </summary>
            public NativeArray<float> BeizerTotalLength;

            public void Execute()
            {
                for (int pathIndex = 0; pathIndex < PathCount; pathIndex++)
                {
                    Vector3 curNode = PathPointArray[pathIndex];
                    Vector3 nextNode = PathPointArray[pathIndex + 1];
                    Vector3 bezierNode = BeizerPointArray[pathIndex];
                    float length = BeizerUilityTools.BezierLength(curNode, nextNode, bezierNode,50);
                    BeizerTotalLength[0] = BeizerTotalLength[0] + length;
                }
            }
        }

        /// <summary>
        /// 计算UV值中V值的总长度
        /// </summary>
        [BurstCompile]
        public struct MeshVTotalLength : IJobParallelFor
        {
            /// <summary>
            /// 模型模板顶点结合
            /// </summary>
            [ReadOnly]
            public NativeArray<float3> TempletVerticeArray;

            /// <summary>
            /// 网格的顶点集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [ReadOnly]
            public NativeArray<float3> VeticesArray;


            /// <summary>
            /// 顶点数上限
            /// </summary>
            [ReadOnly]
            public int CurrentVerticeCount;


            /// <summary>
            /// 网格的V值的顶点集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeArray<float> VTotalLength;

            /// <summary>
            /// 每个顶点的距离集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeArray<float> EachVDistanceLength;

            public void Execute(int templteIndex)
            {
                //计算V值方向的距离总长度
                float totalDistance = 0;
                EachVDistanceLength[templteIndex] = 0;
                for (int vIndex = templteIndex; vIndex < CurrentVerticeCount; vIndex += TempletVerticeArray.Length)
                {
                    int nextIndex = vIndex + TempletVerticeArray.Length;
                    if (nextIndex > (CurrentVerticeCount - 1)) break;
                    float3 curPoint = VeticesArray[vIndex];
                    float3 nextPoint = VeticesArray[nextIndex];
                    totalDistance += CaculateSqrDistance(curPoint, nextPoint);
                    EachVDistanceLength[nextIndex] = totalDistance;
                }
                VTotalLength[templteIndex] = totalDistance;
            }
        }

        /// <summary>
        /// 动态网格UV值计算
        /// </summary>
        [BurstCompile]
        public struct MeshUVCaculate : IJobParallelForBatch
        {
            /// <summary>
            /// 网格的顶点集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [ReadOnly]
            public NativeArray<float3> VeticesArray;

            /// <summary>
            /// 网格的V值的顶点集合
            /// </summary>
            [ReadOnly]
            public NativeArray<float> VTotalLength;

            /// <summary>
            /// 每个顶点的距离集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [ReadOnly]
            public NativeArray<float> EachVDistanceLength;

            /// <summary>
            /// 网格模板顶点总数
            /// </summary>
            [ReadOnly]
            public int TemplteVerticesCount;

            /// <summary>
            /// 网格的UV集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            public NativeArray<float2> UVArray;

            public void Execute(int startIndex, int count)
            {
                for (int verticeIndex = startIndex; verticeIndex < (startIndex + count - 1); verticeIndex++)
                {
                    int index = verticeIndex % TemplteVerticesCount;
                    float t = EachVDistanceLength[verticeIndex] / VTotalLength[index];
                    UVArray[verticeIndex] = new float2(UVArray[verticeIndex].x, t);
                }
            }
        }

        /// <summary>
        /// 动态网格三角面计算
        /// </summary>
        [BurstCompile]
        public struct MeshTriangleCaculate : IJobParallelFor
        {

            /// <summary>
            /// 顶点总数
            /// </summary>
            [ReadOnly]
            public int TemplteVerticesCount;

            /// <summary>
            /// 三角形序号集合
            /// </summary>
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeArray<int> TriangleArray;

            public void Execute(int pathIndex)
            {
                for (int pointIndex = 0; pointIndex < TemplteVerticesCount - 1; pointIndex++)
                {
                    int triangleIndex = TemplteVerticesCount * pathIndex * 6 + pointIndex * 6;
                    int currentOne = TemplteVerticesCount * pathIndex + pointIndex;
                    int currentTwo = TemplteVerticesCount * pathIndex + (pointIndex + 1) % (TemplteVerticesCount);
                    int nextOne = TemplteVerticesCount * (pathIndex + 1) + pointIndex;
                    int nextTwo = TemplteVerticesCount * (pathIndex + 1) + (pointIndex + 1) % (TemplteVerticesCount);

                    TriangleArray[triangleIndex] = currentOne;
                    TriangleArray[triangleIndex + 1] = currentTwo;
                    TriangleArray[triangleIndex + 2] = nextOne;
                    TriangleArray[triangleIndex + 3] = nextOne;
                    TriangleArray[triangleIndex + 4] = currentTwo;
                    TriangleArray[triangleIndex + 5] = nextTwo;
                }
            }
        }

        private static HighPerformanceMeshTemplet HighEffectParseModelTemplet(TempletObjectBase modelTemplet)
        {
            int vecticesCount = modelTemplet.Vectices.Count;
            int triangleCount = modelTemplet.Triangles.Count;

            float3[] vertices = new float3[vecticesCount];
            float2[] UV = new float2[vecticesCount];
            float3[] normal = new float3[vecticesCount];
            int[] triangles = new int[triangleCount * 3];
            for (int i = 0; i < vecticesCount; i++)
            {
                VerticesData data = modelTemplet.Vectices[i];
                vertices[i] = data.Vertices;
                UV[i] = data.UV;
                normal[i] = data.Normal;
            }
            int triangleIndex = 0;
            for (int j = 0; j < triangleCount; j++)
            {
                TriangelData data = modelTemplet.Triangles[j];
                triangles[triangleIndex] = data.FirstVertice;
                triangles[triangleIndex + 1] = data.SecondVertice;
                triangles[triangleIndex + 2] = data.ThridVertice;
                triangleIndex += 3;
            }
            return new HighPerformanceMeshTemplet(vertices, triangles, normal, UV);
        }

        private static bool TranslationPathListAndBeizerList(List<Vector3> pathList, List<Vector3> beizerList, out NativeArray<float3> PathNativeArray, out NativeArray<float3> BeizerNativeArray)
        {
            bool flage = true;
            if (pathList != null && beizerList != null)
            {
                PathNativeArray = new NativeArray<float3>(pathList.Count, Allocator.TempJob);
                BeizerNativeArray = new NativeArray<float3>(beizerList.Count, Allocator.TempJob);
                for (int pathIndex = 0; pathIndex < pathList.Count; pathIndex++ ) {
                    PathNativeArray[pathIndex] = pathList[pathIndex];
                }
                for (int beizerIndex = 0; beizerIndex < beizerList.Count; beizerIndex++)
                {
                    BeizerNativeArray[beizerIndex] = beizerList[beizerIndex];
                }
            }
            else
            {
                PathNativeArray = new NativeArray<float3>(0, Allocator.TempJob);
                BeizerNativeArray = new NativeArray<float3>(0, Allocator.TempJob);
                flage = false;
            }
            return flage;
        }

        private static Vector3 localToWorld(Vector3 localPos, Vector3 parentPos, Vector3 forward)
        {
            if (forward == Vector3.zero)
            {
                return parentPos;
            }
            else
            {
                return parentPos + Quaternion.LookRotation(forward) * localPos;
            }
        }

        private static Vector3 localToWorld(float3 localPos, float3 parentPos, float3 forward, float3 DIRECTIONUP)
        {
            if (forward.Equals(float3.zero) )
            {
                return parentPos;
            }
            else
            {
                return parentPos + math.rotate(quaternion.LookRotation(forward, DIRECTIONUP), localPos);
            }
        }

        private static float CaculateSqrDistance(float3 start, float3 end)
        {
            float valueX = (start.x - end.x);
            float valutY = (start.y - end.y);
            float valutZ = (start.z - end.z);
            return valueX * valueX + valutY * valutY + valutZ * valutZ;
        }


        private static List<int> GetSubTriangelsList(int verticeCount, HighPerformanceMeshTemplet meshTemplet, int tCapacity)
        {
            List<int> subMeshTriangels = new List<int>(tCapacity);
            for (int triangelIndex = 0; triangelIndex < meshTemplet.Triangels.Length; triangelIndex += 3)
            {
                subMeshTriangels.Add(verticeCount + meshTemplet.Triangels[triangelIndex]);
                subMeshTriangels.Add(verticeCount + meshTemplet.Triangels[triangelIndex + 1]);
                subMeshTriangels.Add(verticeCount + meshTemplet.Triangels[triangelIndex + 2]);
            }

            return subMeshTriangels;
        }

        private static void AddMaskTemplet(HighPerformanceMeshTemplet meshTemplet, MeshShell tunnelShell, Vector3 curPosition, Vector3 firstGradient, ref int verticeIndex)
        {
            for (int verticesIndex = 0; verticesIndex < meshTemplet.Vertices.Length; verticesIndex++)
            {
                Vector3 localPos = meshTemplet.Vertices[verticesIndex];
                Vector3 worldPos = localToWorld(localPos, curPosition, firstGradient);
                tunnelShell.AddVertices(verticeIndex, worldPos);
                tunnelShell.AddNormals(verticeIndex, meshTemplet.Normals[verticesIndex]);
                tunnelShell.AddUV(verticeIndex, meshTemplet.UV[verticesIndex]);
                verticeIndex++;
            }
        }


        /// <summary>
        /// 多线程版本生成动态连续网格
        /// </summary>
        /// <param name="TemplteMesh"></param>
        /// <param name="pathList"></param>
        /// <param name="beizerList"></param>
        /// <param name="finishCallback"></param>
        /// <param name="textrueType"></param>
        /// <param name="segment"></param>
        public static void DynamicSpwanModelWithHighPerformance(TempletObjectBase TemplteMesh, List<Vector3> pathList, List<Vector3> beizerList, Action<MeshPlus, Material[], Vector2> finishCallback, TextureType textrueType = TextureType.Texture, int segment = 10)
        {
            HighPerformanceMeshTemplet meshTemplet = HighEffectParseModelTemplet(TemplteMesh);
            NativeArray<float3> pathNatvieArray;
            NativeArray<float3> beizerNativeArray;
            if (!TranslationPathListAndBeizerList(pathList, beizerList, out pathNatvieArray, out beizerNativeArray))
            {
                Debug.LogError("路径点集合与曲线集合不能空！");
                return;
            }
            int templteVerticesCount = meshTemplet.Vertices.Length;
            int templteTriangleCapcity = meshTemplet.Triangels.Length;
            int pathCount = pathList.Count - 1;
            int vCapacity = templteVerticesCount * (pathCount * (segment - 1) + 3);
            int tCapacity = templteVerticesCount * (pathCount * (segment - 2) + pathCount) * 6;
            int vCurrentCapacity = templteVerticesCount * (pathCount * (segment - 1) + 1);
            int sCapacity = 2;
            int pathNodeCount = pathCount + (pathCount) * (segment - 2);
            Material surfaceMat = TemplteMesh.SurfaceMaterial;
            Material sectionMat = TemplteMesh.SectionMaterial;
            float UTotalLength = BeizerUilityTools.GetTotalLength(meshTemplet.Vertices);
            MeshShell tunnelShell = new MeshShell(vCapacity, tCapacity, sCapacity);
            NativeArray<float3> verticesNativeArray = new NativeArray<float3>(vCapacity, Allocator.TempJob);
            NativeArray<float3> normalNativeArray = new NativeArray<float3>(vCapacity, Allocator.TempJob);
            NativeArray<float2> UVNativeArray = new NativeArray<float2>(vCapacity, Allocator.TempJob);
            NativeArray<int> triangleNativeArray = new NativeArray<int>(tCapacity, Allocator.TempJob);
            NativeArray<float> VTotalLengthNativeArray = new NativeArray<float>(templteVerticesCount, Allocator.TempJob);
            NativeArray<float> EachVDistanceLength = new NativeArray<float>(vCurrentCapacity, Allocator.TempJob);
            NativeArray<float> bezierLength = new NativeArray<float>(1, Allocator.TempJob);

            MeshVerticesCaculate meshVerticesCaculateJob = new MeshVerticesCaculate()
            {
                PathPointArray = pathNatvieArray,
                BeizerPointArray = beizerNativeArray,
                TempletVerticeArray = meshTemplet.Vertices,
                SegmentCount = segment,
                UTotalLength = UTotalLength,
                VeticesArray = verticesNativeArray,
                NormalArray = normalNativeArray,
                UVArray = UVNativeArray,
                DIRECTIONUP = new float3(0,1,0)
            };
            BeizerCurveLength curveLengthJob = new BeizerCurveLength()
            {
                PathPointArray = pathNatvieArray,
                BeizerPointArray = beizerNativeArray,
                PathCount = pathCount,
                BeizerTotalLength = bezierLength
            };

            MeshVTotalLength VTotalLengthJob = new MeshVTotalLength()
            {
                TempletVerticeArray = meshTemplet.Vertices,
                VeticesArray = verticesNativeArray,
                CurrentVerticeCount = vCurrentCapacity,
                VTotalLength = VTotalLengthNativeArray,
                EachVDistanceLength = EachVDistanceLength
            };
            MeshUVCaculate UVCaculateJob = new MeshUVCaculate()
            {
                VeticesArray = verticesNativeArray,
                VTotalLength = VTotalLengthNativeArray,
                TemplteVerticesCount = templteVerticesCount,
                UVArray = UVNativeArray,
                EachVDistanceLength = EachVDistanceLength
            };
            MeshTriangleCaculate meshTriangleCaculateJob = new MeshTriangleCaculate()
            {
                TemplteVerticesCount = templteVerticesCount,
                TriangleArray = triangleNativeArray,
            };
            JobHandle meshVerticeHandle = meshVerticesCaculateJob.ScheduleBatch(pathNatvieArray.Length, pathNatvieArray.Length > 4 ? pathNatvieArray.Length / 4 : pathNatvieArray.Length);
            JobHandle cureLengthHandle = curveLengthJob.Schedule(meshVerticeHandle);
            JobHandle VToatalLengthHandle = VTotalLengthJob.Schedule(templteVerticesCount, templteVerticesCount > 4 ? templteVerticesCount / 4 : templteVerticesCount, meshVerticeHandle);
            JobHandle dependencies = JobHandle.CombineDependencies(meshVerticeHandle, VToatalLengthHandle);
            JobHandle UVCaculateHandle = UVCaculateJob.ScheduleBatch(vCurrentCapacity, vCurrentCapacity > 4 ? vCurrentCapacity / 4 : vCurrentCapacity, dependencies);
            JobHandle meshTriangleHandle = meshTriangleCaculateJob.Schedule(pathNodeCount, pathNodeCount > 4 ? pathNodeCount / 4 : pathNodeCount);

            JobHandle.CompleteAll(ref UVCaculateHandle, ref meshTriangleHandle, ref cureLengthHandle);
            //Debug.Log("<color=green>"+ curveLengthJob.BeizerTotalLength[0]+" vs "+UTotalLength+"</color>");
            tunnelShell.AddNativeArray(verticesNativeArray, normalNativeArray, UVNativeArray, triangleNativeArray);

            List<int> forwardSubMeshTriangels = GetSubTriangelsList(vCurrentCapacity, meshTemplet, templteTriangleCapcity);
            Vector3 firstGradient = -BeizerUilityTools.BezierCurveGradient(pathList[0], pathList[1], beizerList[0], 0);
            AddMaskTemplet(meshTemplet, tunnelShell, pathList[0], firstGradient, ref vCurrentCapacity);
            tunnelShell.AddSubTriangles(0, forwardSubMeshTriangels);

            List<int> backSubMeshTriangels = GetSubTriangelsList(vCurrentCapacity, meshTemplet, templteTriangleCapcity);
            Vector3 backGradient = BeizerUilityTools.BezierCurveGradient(pathList[pathList.Count - 2], pathList[pathList.Count - 1], beizerList[pathList.Count - 2], 1);
            AddMaskTemplet(meshTemplet, tunnelShell, pathList[pathList.Count - 1], backGradient, ref vCurrentCapacity);
            tunnelShell.AddSubTriangles(1, backSubMeshTriangels);

            MeshPlus mesh = tunnelShell.GetMesh(meshTemplet, segment);
            Material[] materials = new Material[] {
               surfaceMat,
               sectionMat,
               sectionMat
            };
            Vector2 textureTile = Vector2.one;
            if (textrueType == TextureType.Tile)
                textureTile = new Vector2(UTotalLength, curveLengthJob.BeizerTotalLength[0]);
            else
                textureTile = new Vector2(1, curveLengthJob.BeizerTotalLength[0] / UTotalLength);

            if (finishCallback != null)
                finishCallback(mesh, materials, textureTile);

            //释放内存
            meshTemplet.Dispose();
            pathNatvieArray.Dispose();
            beizerNativeArray.Dispose();
            verticesNativeArray.Dispose();
            normalNativeArray.Dispose();
            UVNativeArray.Dispose();
            VTotalLengthNativeArray.Dispose();
            EachVDistanceLength.Dispose();
            triangleNativeArray.Dispose();
            bezierLength.Dispose();
        }
    }
}

